﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Ink;
using System.Windows.Input;
using BitsOfStuff.MyActions;
using WindowsInput;

/// <summary>
/// Namespace for Support functions
/// </summary>
namespace BitsOfStuff.Support
{
    internal class SupportFunctions
    {
        public void addStartEnd(ref bool[] bitMap)
        {
            bitMap[0] = true;
            bitMap[bitMap.Length - 1] = true;
        }

        public static bool[] makeStartEnd(int length)
        {
            bool[] result = new bool[length];
            result[0] = true;
            result[length - 1] = true;
            return result;
        }

        private static bool[] mergeBitMaps(List<bool[]> bitMaps)
        {
            bool[] result = new bool[bitMaps[1].Length];
            for (int i = 0; i < bitMaps.Count; i++)
            {
                for (int j = 0; j < bitMaps[i].Length; j++)
                {
                    result[j] = result[j] || bitMaps[i][j];
                }
            }
            return result;
        }

        public static bool[] mergeNearPoints(List<bool[]> bitMaps, int interval)
        {
            var bitMap = mergeBitMaps(bitMaps);
            return mergeNearPoints(bitMap, interval);
        }

        private static bool[] mergeNearPoints(bool[] bitMap, int interval)
        {
            bool[] result = new bool[bitMap.Length];
            for (int i = 0; i < bitMap.Length; i++)
            {
                if (bitMap[i])
                {
                    int newPointIndex = findNearestPoint(bitMap, i, interval);
                    result[newPointIndex] = true;
                    i = newPointIndex + interval;
                }
            }
            return result;
        }

        public static MyPoint[] makeMyPointsToArray(Stroke stroke)
        {
            var result = new MyPoint[stroke.StylusPoints.Count];
            int length = result.Length;
            for (int i = 0; i < length; i++)
            {
                result[i].create(stroke.StylusPoints[i]);
            }
            return result;
        }

        public static List<MyPoint> makeMyPointsToList(Stroke stroke)
        {
            var result = new List<MyPoint>();
            for (int i = 0; i < stroke.StylusPoints.Count; i++)
            {
                result.Add(MyPoint.makePoint(stroke.StylusPoints[i]));
            }
            return result;
        }

        private static int findNearestPoint(bool[] bitMap, int index, int interval)
        {
            int result = index;
            for (int i = index + 1; i < interval; i++)
            {
                if (bitMap[i])
                {
                    result = (index + i + findNearestPoint(bitMap, index + i, interval)) / 3;
                    return result;
                }
            }
            return index;
        }

        public static bool[] simpleBiteMapMarge(bool[] map1, bool[] map2)
        {
            bool[] result = new bool[map1.Length];
            for (int i = 0; i < map1.Length; i++)
            {
                result[i] = map1[i] || map2[i];
            }
            return result;
        }

        public static void drainedList(int radius, List<int> upperList, ref List<int> drainedList)
        {
            drainedList.RemoveAll(a => upperList.Exists(w => ((a > w - radius)
                                                          && (a < w + radius))));
        }

        public static void drainedList(int radius, ref List<int> bottomList)
        {
        }

        public static List<int> bitMapToIndex(bool[] bitMap)
        {
            List<int> result = new List<int>();
            for (int i = 0; i < bitMap.Length; i++)
            {
                if (bitMap[i])
                    result.Add(i);
            }
            return result;
        }

        public static bool[] indexToBitMap(List<int> indexes, int length)
        {
            bool[] result = new bool[length];
            foreach (int index in indexes)
            {
                if (index < length)
                    result[index] = true;
            }
            return result;
        }

        public static List<bool[]> finalSmoothing(MyPoint[] myPoints, bool[] cornerBitMap, bool[] curveBitMap, bool[] dpBitMap)
        {
            int resultLength = cornerBitMap.Length;
            List<int> startEndList = new List<int>();
            startEndList.Add(0);
            startEndList.Add(resultLength - 1);
            List<int> cornerList = bitMapToIndex(cornerBitMap);
            List<int> curvedList = bitMapToIndex(curveBitMap);
            List<int> dpList = bitMapToIndex(dpBitMap);

            drainedList(MyParameters.curvedMargetDistance, startEndList, ref cornerList);
            drainedList(MyParameters.curvedMargetDistance, startEndList, ref curvedList);
            drainedList(MyParameters.curvedMargetDistance, startEndList, ref dpList);

            drainedList(MyParameters.curvedMargetDistance, cornerList, ref curvedList);
            drainedList(MyParameters.curvedMargetDistance, cornerList, ref dpList);

            List<int> hightCurvedPoints = new List<int>();
            hightCurvedPoints.AddRange(startEndList);
            hightCurvedPoints.AddRange(cornerList);

            hightCurvedPoints.Sort();

            //Remove humanCircue
            dpList = humanHnadCirceCompensation(myPoints, hightCurvedPoints, dpList);
            dpList.Sort();

            List<bool[]> result = new List<bool[]>();
            result.Add(makeStartEnd(resultLength));
            result.Add(indexToBitMap(cornerList, resultLength));
            result.Add(indexToBitMap(dpList, resultLength));

            return result;
        }

        public static List<MyLine> pipeCompensation(List<MyLine> myLines)
        {
            double avrgLength =
                myLines.Aggregate(new double(), (current, myLine) => current + myLine.Vector.getVectorLength()) /
                myLines.Count() * MyParameters.pipeDistance;
            return myLines.Where(myLine => myLine.Vector.getVectorLength() > avrgLength).ToList();
        }

        public static bool[] distanceSmoothing(bool[] bitMap, MyPoint[] myPoints, double radius)
        {
            List<int> indexes = bitMapToIndex(bitMap);
            for (int i = 0; i < indexes.Count; i++)
            {
                indexes.RemoveAll(near => isPointNearInSomeWay(myPoints[indexes[i]], myPoints[near], radius));
            }
            return indexToBitMap(indexes, bitMap.Length);
        }

        public static bool isPointNearInSomeWay(MyPoint pointA, MyPoint pointB, double radius)
        {
            if (Math.Abs(pointA.getX() - pointB.getX()) < radius)
                return true;
            if (Math.Abs(pointA.getY() - pointB.getY()) < radius)
                return true;
            return false;
        }

        public static List<MySelectedPoint> makeSelectedPoint(List<bool[]> bitMaps, MyPoint[] myPoints)
        {
            if (bitMaps.Count == 1) // is short object
            {
                switch (bitMaps[0].Length)
                {
                    case 1:  // if its only dot
                        {
                            return new List<MySelectedPoint> { new ShortPoint(myPoints.FirstOrDefault()) };
                        }
                    case 2:  // If its only long
                        {
                            return new List<MySelectedPoint>
                            {
                                new ShortPoint(myPoints.FirstOrDefault()),
                                new ShortPoint(myPoints.LastOrDefault())
                            };
                        }
                    case 3: // If its only mekcen
                        {
                            return new List<MySelectedPoint>
                            {
                                new ShortPoint(myPoints.FirstOrDefault()),
                                new ShortPoint(myPoints[(int)myPoints.Length/2]),
                                new ShortPoint(myPoints.LastOrDefault())
                            };
                        }
                    default:
                        break;
                }
            }

            MySelectedPoint addedPoint;
            List<MySelectedPoint> result = new List<MySelectedPoint>();
            int currentIndex = 0;
            addedPoint = new StartEndPoint(myPoints[0]);
            addedPoint.setIndex(currentIndex);
            result.Add(addedPoint);
            currentIndex++;

            for (int i = 0; i < bitMaps[0].Length; i++)
            {
                if (bitMaps[1][i])
                {
                    addedPoint = new CordnerPoint(myPoints[i]);
                    addedPoint.setIndex(currentIndex);
                    currentIndex++;
                    result.Add(addedPoint);
                }
                else if (bitMaps[2][i])
                {
                    addedPoint = new CurvePoint(myPoints[i]);
                    addedPoint.setIndex(currentIndex);
                    currentIndex++;
                    result.Add(addedPoint);
                }
            }
            addedPoint = new StartEndPoint(myPoints[myPoints.Length - 1]);
            addedPoint.setIndex(currentIndex);
            result.Add(addedPoint);

            return result;
        }

        public static List<MySelectedPoint> makeSelectedPoint(bool[] bitMap, MyPoint[] myPoints)
        {
            MySelectedPoint addedPoint;
            List<MySelectedPoint> result = new List<MySelectedPoint>();
            int currentIndex = 0;
            addedPoint = new StartEndPoint(myPoints[0]);
            addedPoint.setIndex(currentIndex);
            result.Add(addedPoint);
            currentIndex++;
            for (int i = 0; i < bitMap.Length; i++)
            {
                if (bitMap[i])
                {
                    addedPoint = new CordnerPoint(myPoints[i]);
                    addedPoint.setIndex(currentIndex);
                    currentIndex++;
                    result.Add(addedPoint);
                }
            }
            return result;
        }

        public static List<int> humanHnadCirceCompensation(MyPoint[] myPoints, List<int> highCurvedPoints, List<int> curvedPoints)
        {
            List<int> result = new List<int>(myPoints.Length);

            for (int i = 0; i < highCurvedPoints.Count - 1; i++)
            {
                MyPoint virtualVectorStartPoint = myPoints[highCurvedPoints[i]];
                MyPoint virtualVectorEndPoint = myPoints[highCurvedPoints[i + 1]];
                MyVector virtualVector = new MyVector();
                virtualVector.makeVector(virtualVectorStartPoint, virtualVectorEndPoint);
                foreach (int curvedPoint in curvedPoints)
                {
                    //if selected points is between start/end point of vector
                    if (highCurvedPoints[i] < curvedPoint && highCurvedPoints[i + 1] > curvedPoint)
                    {
                        double distance = virtualVectorStartPoint.getDistanceToPoint(myPoints[curvedPoint]) +
                                          virtualVectorEndPoint.getDistanceToPoint(myPoints[curvedPoint]);
                        double maxAlowDistance = virtualVector.getVectorLength() *
                                                 MyParameters.supportHandParabolTolerance;

                        if (distance > maxAlowDistance)
                        {
                            result.Add(curvedPoint);
                        }
                    }
                    else
                    {
                        // If dp point is larger that end point of vector, so any other will be larger
                        if (curvedPoint > highCurvedPoints[i + 1])
                            break;
                    }
                }
            }

            return result;
        }

        public static string getFileName(string name)
        {
            char[] separator = { '\\' };
            string[] parst = name.Split(separator);
            return parst.LastOrDefault().Remove(parst.LastOrDefault().Length - 5, 5);
        }

        public static string convertFromSequenceToString(List<int> findSequence)
        {
            StringBuilder result = new StringBuilder();
            foreach (int i in findSequence)
            {
                if (i != -1)
                    result.Append(i);
                else
                {
                    result.Append(" | ");
                }
            }
            return result.ToString();
        }

        public static bool compareList(List<int> a, List<int> b)
        {
            if (a.Count != b.Count)
            {
                return false;
            }
            return !a.Where((t, i) => t != b[i]).Any();
        }

        public static List<MyAction> intersectActions(List<MyAction> a, List<MyAction> b)
        {
            var result = new List<MyAction>();
            if (a == null && b == null)
                return new List<MyAction>();

            if (a == null)
                return b;

            if (b == null)
                return a;

            foreach (MyAction myActionB in b)
            {
                foreach (MyAction myActionA in a)
                {
                    if (myActionA.Equals(myActionB))
                    {
                        result.Add(myActionA);
                        break;
                    }
                }
            }
            return result;
        }

        public static List<StylusPoint> getIntersectPoint(Stroke stroke)
        {
            double EPSILON = 1;
            var result = new List<StylusPoint>();
            return result;
        }

        public static IEnumerable<IEnumerable<T>> cartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
            if (sequences.Count() > 15)
            {
                return emptyProduct;
            }
            return sequences.Aggregate(
              emptyProduct,
              (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item }));
        }

        public static List<int> generetaFullSequence(int length)
        {
            var result = new List<int>(length);
            for (int i = 1; i < length + 1; i++)
            {
                result.Add(1);
            }
            return result;
        }

        private static int lewenstainDistance(string a, string b)
        {
            if (String.IsNullOrEmpty(a) || String.IsNullOrEmpty(a))
                return 0;

            var table = new int[a.Length, b.Length];
            for (int i = 0; i < a.Length; i++)
            {
                table[i, 0] = i;
            }
            for (int j = 0; j < b.Length; j++)
            {
                table[0, j] = j;
            }

            for (int j = 1; j < b.Length; j++)
            {
                for (int i = 1; i < a.Length; i++)
                {
                    if (a[i] == b[j])
                        table[i, j] = table[i - 1, j - 1];
                    else
                    {
                        table[i, j] = new List<int>()
                                          {
                                          table[i - i, j] + 1, //deletion
                                          table[i,j-1] + 1,  // insertion
                                          table[i-1,j-1] + 1 // substitucia
                                          }.Min();
                    }
                }
            }

            return table[a.Length - 1, b.Length - 1];
        }

        public static int calculateDistance(string a, string b)
        {
            var separators = new char[1];
            separators[0] = '|';

            string[] aArray = a.Split(separators);
            string[] bArray = b.Split(separators);

            if (aArray.Length != bArray.Length)
                return int.MaxValue;
            var distance = new int();

            for (int i = 0; i < aArray.Length; i++)
            {
                if (aArray[i] != null && bArray[i] != null)
                    distance = distance + lewenstainDistance(aArray[i], bArray[i]);
            }

            return distance;
        }

        public static MyPoint rotate(MyPoint point, MyPoint origin, double angle)
        {
            double radianAngle = angle * (Math.PI / 180);
            MyPoint result = new MyPoint();
            double resultX = Math.Cos(radianAngle) * (point.getX() - origin.getX()) -
                             Math.Sin(radianAngle) * (point.getY() - origin.getY()) + origin.getX();

            double resultY = Math.Sin(radianAngle) * (point.getX() - origin.getX()) +
                             Math.Cos(radianAngle) * (point.getX() - origin.getX()) + origin.getY();

            result.create(resultX, resultY);
            return result;
        }

        public static double calculateDistance(StylusPoint a, StylusPoint b)
        {
            double dx = b.X - a.X;
            double dy = b.Y - a.Y;

            return Math.Sqrt(dx * dx + dy * dy);
        }

        public static int[] findIntersect(Stroke stroke)
        {
            const int lengthOfSegment = 2;
            var partialLines = new List<StreingtMyLine>();
            MyPoint a = new MyPoint();
            MyPoint b = new MyPoint();

            for (int i = 0; i < stroke.StylusPoints.Count - lengthOfSegment; i = i + lengthOfSegment)
            {
                a.create(stroke.StylusPoints[i]);
                b.create(stroke.StylusPoints[i + lengthOfSegment]);
                partialLines.Add(new StreingtMyLine(new MySelectedPoint(a), new MySelectedPoint(b)));
            }

            var result = new List<int>();
            for (int i = 4; i < partialLines.Count; i++)
            {
                for (int j = i + 2; j < partialLines.Count; j++)
                {
                    if (processIntersection(partialLines[i], partialLines[j]))
                    {
                        result.Add(i * lengthOfSegment + lengthOfSegment / 2);
                        processIntersection(partialLines[i], partialLines[j]);
                    }
                }
            }
            return result.ToArray();
        }

        private static bool processIntersection(MyLine a, MyLine b)
        {
            MyPoint point4 = a.Vector.getEndPoint().getPoint();
            MyPoint point3 = a.Vector.getStartPoint().getPoint();

            MyPoint point2 = b.Vector.getEndPoint().getPoint();
            MyPoint point1 = b.Vector.getStartPoint().getPoint();

            double ua = (point4.X - point3.X) * (point1.Y - point3.Y) - (point4.Y - point3.Y) * (point1.X - point3.X);
            double ub = (point2.X - point1.X) * (point1.Y - point3.Y) - (point2.Y - point1.Y) * (point1.X - point3.X);
            double denominator = (point4.Y - point3.Y) * (point2.X - point1.X) - (point4.X - point3.X) * (point2.Y - point1.Y);

            if (Math.Abs(denominator) <= 0.00001f)
            {
                if (Math.Abs(ua) <= 0.00001f && Math.Abs(ub) <= 0.00001f)
                {
                    //return true;
                }
            }
            else
            {
                ua /= denominator;
                ub /= denominator;

                if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
                {
                    return true;
                }
            }
            return false;
        }

        public static List<String> getTaskWindows()
        {
            var result = new List<string>();
            // Get the desktopwindow handle
            int nDeshWndHandle = NativeWin32.GetDesktopWindow();
            // Get the first child window
            int nChildHandle = NativeWin32.GetWindow(nDeshWndHandle, NativeWin32.GW_CHILD);

            while (nChildHandle != 0)
            {
                //If the child window is this (SendKeys) application then ignore it.
                //if (nChildHandle == this.Handle.ToInt32())
                //{
                //    nChildHandle = NativeWin32.GetWindow(nChildHandle, NativeWin32.GW_HWNDNEXT);
                //}

                // Get only visible windows
                if (NativeWin32.IsWindowVisible(nChildHandle) != 0)
                {
                    StringBuilder sbTitle = new StringBuilder(1024);
                    // Read the Title bar text on the windows to put in combobox
                    NativeWin32.GetWindowText(nChildHandle, sbTitle, sbTitle.Capacity);
                    String sWinTitle = sbTitle.ToString();
                    {
                        if (sWinTitle.Length > 0)
                        {
                            result.Add(sWinTitle);
                        }
                    }
                }
                // Look for the next child.
                nChildHandle = NativeWin32.GetWindow(nChildHandle, NativeWin32.GW_HWNDNEXT);
            }
            return result;
        }

        public static void sendKey(char key, ref bool shift)
        {
            if (key == '↑')
            {
                shift = true;
                return;
            }
            if (key == ' ')
            {
                InputSimulator.SimulateKeyPress(VirtualKeyCode.SPACE);
                InputSimulator.SimulateTextEntry("  ");
                return;
            }
            if (key == '\n')
            {
                InputSimulator.SimulateKeyPress(VirtualKeyCode.SELECT);
                return;
            }
            if (key == '←')
            {
                InputSimulator.SimulateKeyPress(VirtualKeyCode.BACK);
                return;
            }

            int iHandle = NativeWin32.FindWindow(null, MyParameters.selectedWindow);

            NativeWin32.SetForegroundWindow(iHandle);

            if (shift && key != '↑')
            {
                InputSimulator.SimulateTextEntry(key.ToString(CultureInfo.InvariantCulture).ToUpper());
                shift = false;
            }
            else
            {
                InputSimulator.SimulateTextEntry(key.ToString(CultureInfo.InvariantCulture).ToLower());
            }
        }
    }
}